<?php

require_once 'system.inc';

class UnionSet extends _Set
{
    protected $_M_sets;
    //protected $_M_optimized;

    function __construct()
    {
        parent::__construct();

        $this->_M_sets      = array();
        //$this->_M_optimized = true;

        if (count($args = func_get_args()) > 0) {
             foreach ($args as &$arg) {
                if (is_subclass_of($arg, '_Set')) {
                    $this->_append($arg);
                } else {
                    throw new Exception();
                }
            }
        }
    }

    function has_a($element)
    {
        foreach ($this->_M_sets as $set) {
            if ($set->has_a($element)) {
                return true;
            }
        }
        return false;
    }

    function toHTML()
    {
        if (empty($this->_M_sets)) {
            $emptySet = new EmptySet();
            return $emptySet->toHTML();
        }
        $result = '(';
        $not_first = false;
        foreach ($this->_M_sets as $set) {
            if ($not_first) {
                $result .= ' &#8746; ';
            }
            $result .= $set->toHTML();
            $not_first = true;
        }
        $result .= ')';
        return $result;
    }

    function _append($element)
    {
        $this->_M_sets[] = clone $element;
    }

    static function _union($a, $b)
    {
        $this_class = __CLASS__;
        $this_obj = new $this_class;
        $pref = '_';
        $hierarhy_a = get_ancestor_or_self_classes($a, '_Set');
        $hierarhy_b = get_ancestor_or_self_classes($b, '_Set');

        $method = '';
        $direct = true;

        for ($j = count($hierarhy_a) - 1; $j >= 0; $j--) {
            for ($i = count($hierarhy_b) - 1; $i >= 0; $i--) {
                $a_class = $hierarhy_a[$j];
                $b_class = $hierarhy_b[$i];
                $method1 = '_' . $a_class . '_' . $b_class;
                $method2 = '_' . $b_class . '_' . $a_class;
                if (method_exists($this_obj, $method1)) {
                    $method = $method1;
                    break 2;
                } elseif (method_exists($this_obj, $method2)) {
                    $method = $method2;
                    $direct = false;
                    break 2;
                }
            }
        }

        if (empty($method)) {
            throw new Exception();
        }

        $params = array($a, $b);
        if (!$direct) {
            $params = array($b, $a);
        }
        return call_user_func_array(array(__CLASS__, $method), $params);
    }

    protected static function _EmptySet__Set($empty, $set)
    {
        return $set;
    }

    protected static function _FullSet__Set($full, $set)
    {
        return $full;
    }

    protected static function _SingleSet_SingleSet($a, $b)
    {
        if (eq($a->element(), $b->element())) {
            return $a;
        } elseif (lt($a->element(), $b->element())) {
            if (eq(sub($b->element(), $a->element()), 1)) {
                return new RangeSet($a->element(), $b->element());
            } else {
                return new EnumSet($a->element(), $b->element());
            }
        } else {
            if (eq(sub($a->element(), $b->element()), 1)) {
                return new RangeSet($b->element(), $a->element());
            } else {
                return new EnumSet($b->element(), $a->element());
            }
        }
    }

    protected static function _SingleSet_RangeSet($single, $range)
    {
        if ($range->has_a($single->element())) {
            return $range;
        } elseif (lt($single->element(), $range->begin())) {
            if (eq(sub($range->begin(), $single->element()), 1)) {
                return new RangeSet($single->element(), $range->end());
            } else {
                return new UnionSet($single, $range);
            }
        } else {
            if (eq(sub($single->element(), $range->end()), 1)) {
                return new RangeSet($range->begin(), $single->element());
            } else {
                return new UnionSet($range, $single);
            }
        }
    }

    protected static function _SingleSet_UnionSet($single, $union)
    {
        return new UnionSet($single, $union);
    }

    protected static function _RangeSet_EnumSet($range, $enum)
    {
        return new UnionSet($range, $enum);
    }

    protected static function _RangeSet_NotSet($range, $not)
    {
        return new UnionSet($range, $not);
    }
}

class NotSet extends _Set
{
    protected $_M_set;

    function __construct($set)
    {
        parent::__construct();

        if (is_subclass_of($set, '_Set')) {
            $this->_M_set = clone $set;
        } else {
            throw new Exception();
        }
    }

    function has_a($element)
    {
        return ! $this->_M_set->has_a($element);
    }

    function toHTML()
    {
        $result = '&#172;';
        $result .= $this->_M_set->toHTML();
        return $result;
    }
}

function Set($set)
{
    return new UnionSet($set);
}

?>